/*
 * Tezad - Classic puzzle game
 * Copyright 2009 Shayne Riley and Paul Maseberg
 *
 * Tezad is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Tezad is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Tezad.  If not, see <http://www.gnu.org/licenses/>
 * or write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * To contact the authors on questions or issues regarding the license,
 * you may send an email to <pezads at gmail dot com>
 */

#include "GraphicsWell.h"

#include <GL/gl.h>

GraphicsWell::GraphicsWell(Subject* inCore, int xOffset, int yOffset, int height, int width)
    : mXOffset(xOffset), mYOffset(yOffset), mHeight(height), mWidth(width),
    mStartingRow(20), mStartingCol(4)
{
    // Some more default values.
    mWell = new GameWell(22, 10, 2);
    mActiveMino = &GameMinoStruct::mino(0);
    mMinoState = GameMinoState(mStartingRow, mStartingCol, 0);
    mMinoEngine = GameMinoEngine();

    // Whoa, wait, are we supposed to pass around the this pointer in ctor?
    inCore->attach(makeFunctor((Functor1<Command&> *)0, *this, &GraphicsWell::update));
}

GraphicsWell::~GraphicsWell()
{
    // We might also need to detach from the game core too... oh well.
    delete mWell;
}

void GraphicsWell::drawWell()
{
    for (int j = 0; j < mWell->height(); ++j)
    {
        for (int i = 0; i < mWell->width(); ++i)
        {
            drawBrick(j, i, mWell->brick(j, i));
        }
    }

    // Draw ghost tetromino.
    int ghostRows[4];
    int ghostCols[4];
    GameMinoState ghost = mMinoEngine.settingMinoState(*mWell, mMinoState, *mActiveMino);
    mActiveMino->bricks(ghost, ghostRows, ghostCols);
    for (int brick = 0; brick < 4; ++brick)
    {
        drawBrick(ghostRows[brick], ghostCols[brick], mActiveMino->brickStyle(), 1);
    }

    // Draw active tetromino.
    int activeRows[4];
    int activeCols[4];
    mActiveMino->bricks(mMinoState, activeRows, activeCols);
    for (int brick = 0; brick < 4; ++brick)
    {
        drawBrick(activeRows[brick], activeCols[brick], mActiveMino->brickStyle(), 2);
    }

    glColor3f(1.0f, 1.0f, 1.0f);
    glBegin(GL_LINE_STRIP);
        glVertex2f((GLfloat)mXOffset, (GLfloat)(mYOffset + mHeight));
        glVertex2f((GLfloat)mXOffset, (GLfloat)mYOffset);
        glVertex2f((GLfloat)(mXOffset + mWidth), (GLfloat)mYOffset);
        glVertex2f((GLfloat)(mXOffset + mWidth), (GLfloat)(mYOffset + mHeight));
    glEnd();

}

void GraphicsWell::drawBrick(int row, int col, int brickType, int style)
{
    float alpha = 1.0f;

    // Ghost block
    if (style == 1)
        alpha = 0.333333f;

    float r = 0.0f, g = 0.0f, b = 0.0f;

    if (brickType == 1) // I mino = cyan
        r = 0.0f, g = 1.0f, b = 1.0f;
    else if (brickType == 2) // J mino = blue
        r = 0.0f, g = 0.0f, b = 1.0f;
    else if (brickType == 3) // L mino = orange
        r = 1.0f, g = 0.333333f, b = 0.0f;
    else if (brickType == 4) // O mino = yellow
        r = 1.0f, g = 1.0f, b = 0.0f;
    else if (brickType == 5) // S mino = green
        r = 0.0f, g = 1.0f, b = 0.0f;
    else if (brickType == 6) // T mino = purple
        r = 1.0f, g = 0.0f, b = 1.0f;
    else if (brickType == 7) // Z mino = red
        r = 1.0f, g = 0.0f, b = 0.0f;

    if (style == 2) {
        r += 0.333333f;
        g += 0.333333f;
        b += 0.333333f;
    }
    glColor4f(r, g, b, alpha);

    GLfloat xScale = (GLfloat)mWidth / (GLfloat)mWell->width();
    GLfloat yScale = (GLfloat)mHeight / (GLfloat)mWell->visibleHeight();

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glRectf((GLfloat)(mXOffset + xScale * (col + 0.05)),
       (GLfloat)(mYOffset + yScale * (row + 0.05)),
       (GLfloat)(mXOffset + xScale * (col + 0.95)),
       (GLfloat)(mYOffset + yScale * (row + 0.95)));
    glDisable(GL_BLEND);
}

void GraphicsWell::update(Command &command)
{
    command.execute(*this);
}

void GraphicsWell::shiftLeft()
{
    mMinoState = mMinoState.shiftLeft();
}

void GraphicsWell::shiftRight()
{
    mMinoState = mMinoState.shiftRight();
}

void GraphicsWell::softDown()
{
    mMinoState = mMinoState.shiftDown();
}

void GraphicsWell::hardDown()
{
    mMinoState = mMinoEngine.settingMinoState(*mWell, mMinoState, *mActiveMino);
}

void GraphicsWell::gravityDown()
{
    mMinoState = mMinoState.shiftDown();
}

void GraphicsWell::shiftUp()
{
    mMinoState = mMinoState.shiftUp();
}

void GraphicsWell::rotateCCW()
{
    mMinoState = mMinoState.rotateCCW();
}

void GraphicsWell::rotateCW()
{
    mMinoState = mMinoState.rotateCW();
}

void GraphicsWell::setMino()
{
    mMinoEngine.setMino(*mWell, mMinoState, *mActiveMino);
}

void GraphicsWell::newMino(int minoNum)
{
    mActiveMino = &GameMinoStruct::mino(minoNum);
    mMinoState = GameMinoState(mStartingRow, mStartingCol, 0);
}

void GraphicsWell::clearRows(const int rows[], int numRows)
{
    for (int i = 0; i < numRows; ++i)
    {
        mWell->clearRow(rows[i]);
    }
}

void GraphicsWell::initGame(int width, int totalHeight, int hiddenHeight,
        int startRow, int startCol, int startSpeed)
{
    delete mWell;
    mWell = new GameWell(totalHeight, width, hiddenHeight);
    mStartingRow = startRow;
    mStartingCol = startCol;
    mActiveMino = &GameMinoStruct::mino(0);
    mMinoState = GameMinoState(mStartingRow, mStartingCol, 0);
}
